還記得之前介紹 React 在生成畫面的時候,會照著 render trees 去呈現畫面。而當我們使用判斷式在不同條件下產生不同畫面,就會造成數的改動。這些改動的時候,Component 裡面的 state 是如何保留或者是重置的呢,今天的文章就會來介紹。
今天的文章參考官方文件的:
直接用文章中的範例為例子:
export default function App() {
return (
<div>
<Counter />
<Counter />
</div>
);
}
一個 div
裡面包含了兩個 Counter
component,把這個畫面畫成 render tree 就會長得像:
然後因為 Counter
裡面有綁著自己的 state count
,在 render 的時候會跟著 component 一起生成。因為他們是所在的 tree 的位置不同,都是獨立的,所以當我們點擊 Counter
裡的按鈕,代表新增次數的 count
就會分別增加。
但如果我們用判斷式,設計勾選一個 checkbox 來判斷其中一個 Counter
會不會出現在畫面。先點擊會消失的 Counter
增加他的 count
後,讓他消失後再勾選回來,會發現原本增加的 count
又回歸變成 0
了。
import { useState } from 'react';
export default function App() {
const [showB, setShowB] = useState(true);
return (
<div>
<Counter />
{showB && <Counter />}
<label>
<input
type="checkbox"
checked={showB}
onChange={e => {
setShowB(e.target.checked)
}}
/>
Render the second counter
</label>
</div>
);
}
會因為這樣是當我們讓 Counter
消失的時候,他也會跟著在樹裡被移除,原本儲存的 state 會跟著消失,再重新加回來就是全新一個 Counter
了,畫成圖就會像是:
如果判斷式是的條件裡都是使用同一個 Component,而且 Component 都在同一個位置,state 就會保留,一樣使用文章中的範例:
import { useState } from 'react';
export default function App() {
const [isFancy, setIsFancy] = useState(false);
return (
<div>
{isFancy ? (
<Counter isFancy={true} />
) : (
<Counter isFancy={false} />
)}
<label>
<input
type="checkbox"
checked={isFancy}
onChange={e => {
setIsFancy(e.target.checked)
}}
/>
Use fancy styling
</label>
</div>
);
}
Counter
會根據 isFancy
state 去傳入他的 prop 的 isFancy
,因為 render tree 的 Counter
沒有被移除,所以他的 state 也不會重置,只是會根據 isFancy
產生 rerender 而有不一樣的呈現。
另外要提醒一下 React 不是根據我們回傳的 JSX 架構去判斷會不會重置,而是畫面上的 UI tree,只要 component 還是在同個位置,裡面的 state 都不會被重置。
import { useState } from 'react';
export default function App() {
if (isFancy) {
return (
<div>
<Counter isFancy={true} />
</div>
);
}
return (
<div>
<Counter isFancy={false} />
</div>
);
}
文章中的範例因為 Counter
都是包在 div
裡面,所以他們會在 tree 的同一個位置。
如果在判斷式裡面,會切換不同的 component,這樣再把原本的 component 加回來,裡面的 state 也會跟著被重置。文章中是把 Counter
切換成 p
,這在轉換的時候,會先把 Counter
先從 render tree 刪除,再加上 p
,再次切換回來就是反過來。因為中間有移除的關係,所以重新回來的 Counter
裡的 state 就會重置。
另一個例子是用不同的 Parent 包住相同的 component,這樣也會重置:
{isFancy ? (
<div>
<Counter isFancy={true} />
</div>
) : (
<section>
<Counter isFancy={false} />
</section>
)}
因為 render tree 需要把原本的 div
換成 section
,所以也需要先把原本的從 tree 裡移除再加新的回去,而因為有移除的關係,component 裡的 state 就重置了。
我自己也試了一下如果是不同的 Component (宣告不同名字),但是裡面內容完全一樣,用判斷式 render 的話 state 會不會重置,答案是會,也就是說只要是不同的 component,就算裡面的回傳 JSX 內容一樣,還是會重置。這也是為什麼不推薦在 component 裡面宣告 component,因為這樣每次 render 都會是新宣告的 component,會當成不同的 component 而重置 state。
預設的情況下,React 會保留 state 在同位置的同一個 component 裡,但我們也可以用一些方法去重置裡面的同 component 同位置的 state,文章裡到兩個:
key
給 component第一點舉簡單的例子,就是 render 兩個 component,但用判斷式讓兩個不會同時出現,這樣 React 在判斷的時候會把這兩個 component 判別成在不同的位置。這在判斷式少的時候可以滿快速方便的使用,但當情境開始變多的時候,可能就會變成比較難控管
第二點,之前有介紹在 render 陣列的時候會用 key
去控管,但其實 key
也不一定只能用在陣列上。他其實的功用是讓 React 分辨出每個 component 的位置,所以就算 component 在同一個位置,但因為他們 key 不同就會被判別成是不同的 component 而去移除後加入新的。
操作 key
也很常拿來使用在含有 form
的 component。因為 form
如果我們沒有使用 state 去控制裡面輸入的內容的話,在切換內容時,輸入的內容會還是保持著,因為 React 會判別是同個位置同個 component,所以沒有觸發移除跟新加入,造成使用者的輸入會保留在 tree 裡面。但如果我們加上 key
後,React 就能判別彼此間的不同,而讓輸入的 input
重置。可以根據不同情境切換不同的程式碼。
今天介紹如何在 render 之間保留或重置 component 的 state,有時候為了想要的畫面就需要特別注意這些規則。在讀這篇文章的時候我也學到很多,大家可以多試試看不同情境,看看 React 會怎麼處理。
今天的文章先到這邊,感謝大家耐心地看完,如果有任何問題與建議都歡迎跟我說,明天見,晚安。